package service

import (
	"errors"
	"fmt"
	"math/rand"
	"net"
	"strconv"
	"strings"
	"syscall"
	"time"

	"gitee.com/sansaniot/ssiot-core/facade/env"
	"gitee.com/sansaniot/ssiot-core/httpmvc/service"
	"gitee.com/sansaniot/ssiot-core/logger"
	"github.com/bytedance/sonic"
	"github.com/henrylee2cn/ameda"
	"github.com/jacobsa/go-serial/serial"
	netutil "github.com/shirou/gopsutil/v3/net"
	"github.com/spf13/cast"
	"github.com/tidwall/sjson"
	"gorm.io/gorm"

	"ssdevice/common/constant"
	cDto "ssdevice/common/dto"
	"ssdevice/common/enums"
	"ssdevice/common/middleware/mqtt"
	"ssdevice/common/utils"
	"ssdevice/internal/device/models"
	"ssdevice/internal/device/models/command"
	"ssdevice/internal/device/models/query"
)

type Device struct {
	sysDevModel    *models.SysDev
	devConfigModel *models.SysDevConfig
	devSensorModel *models.SysDevSensor
	devSystemModel *models.SysDevSystem
	devFuncModel   *models.SysDevFunc
	productModel   *models.SysDevProduct
	service.Service
}

func (d *Device) SetDevModel(m *models.SysDev) *Device {
	d.sysDevModel = m
	return d
}

func (d *Device) SetConfigModel(m *models.SysDevConfig) *Device {
	d.devConfigModel = m
	return d
}

func (d *Device) SetSensorModel(m *models.SysDevSensor) *Device {
	d.devSensorModel = m
	return d
}

func (d *Device) SetSystemModel(m *models.SysDevSystem) *Device {
	d.devSystemModel = m
	return d
}

func (d *Device) SetFuncModel(m *models.SysDevFunc) *Device {
	d.devFuncModel = m
	return d
}

func (d *Device) SetProductModel(m *models.SysDevProduct) *Device {
	d.productModel = m
	return d
}

func (d *Device) SetAllModel() {
	d.sysDevModel = &models.SysDev{}
	d.devConfigModel = &models.SysDevConfig{}
	d.devSensorModel = &models.SysDevSensor{}
	d.devSystemModel = &models.SysDevSystem{}
	d.productModel = &models.SysDevProduct{}
	d.devFuncModel = &models.SysDevFunc{}
	d.sysDevModel.Orm = d.Orm
	d.devConfigModel.Orm = d.Orm
	d.devSensorModel.Orm = d.Orm
	d.devSystemModel.Orm = d.Orm
	d.productModel.Orm = d.Orm
	d.devFuncModel.Orm = d.Orm
}

func generateProfile(db *gorm.DB, dev *models.SysDev, formProductConfig models.SysDevConfig, fromProductSensors []models.SysDevSensor, formProductFunc models.SysDevFunc, req *command.DeviceInsertReq) (*models.SysDevConfig, []models.SysDevSensor, *models.SysDevSystem, *models.SysDevFunc) {
	sn := dev.Sn
	devType := dev.Type
	var config, funcs string
	if devType == enums.DevTypeOfDevice {
		// 默认设备模板
		config = constant.DevConfigTemplate
		// 有网关 且 (产品默认 或 无产品)  按网关的下位机配置
		gateBase := len(dev.GatewaySn) > 0 && (dev.ProductModel == constant.DefaultProductCode || len(formProductConfig.Data) == 0)
		if gateBase {
			// 按照gateway生成初始
			configModel := &models.SysDevConfig{}
			configModel.Orm = db
			_ = configModel.FindDevConfig(&query.SysDevConfigQuery{Sn: dev.GatewaySn})
			fromGatewayConfig := utils.GetFieldFromJson([]string{"config", "subdevice"}, utils.BytesToMap(configModel.Data))
			if fromGatewayConfig != nil && len(fromGatewayConfig.(map[string]interface{})) > 0 {
				devConfigTemplateObj := utils.BytesToMap([]byte(config))
				fromGatewayConfigMap := fromGatewayConfig.(map[string]interface{})
				// 跟随网关profile但置空sensor
				fromGatewayConfigMap["sensor"] = map[string]interface{}{}
				devConfigTemplateObj["config"] = fromGatewayConfigMap
				config = string(utils.MapToBytes(devConfigTemplateObj))
			}
		} else if len(formProductConfig.Data) > 0 {
			// 其他情况优先 按产品config （无网关 或 非默认产品）
			config = string(formProductConfig.Data)
		}
		if len(formProductFunc.Data) > 0 {
			// 按产品func
			funcs = string(formProductFunc.Data)
		}
	} else {
		// 默认网关模板
		config = constant.GatewayConfigTemplate
		if len(formProductConfig.Data) > 0 {
			// 按产品config
			config = string(formProductConfig.Data)
		}
		funcs = constant.GatewayFuncTemplate
		if len(formProductFunc.Data) > 0 {
			// 按产品config
			funcs = string(formProductFunc.Data)
		}
	}
	if len(req.ProfileCustom) > 0 {
		for k, v := range req.ProfileCustom {
			config, _ = sjson.Set(config, k, v)
		}
	}
	now := cDto.LocalTime(time.Now())
	sensors := make([]models.SysDevSensor, 0)
	for _, psensor := range fromProductSensors {
		sensors = append(sensors, models.SysDevSensor{
			Id:         utils.SimpleUUID(),
			Sn:         sn,
			Key:        psensor.Key,
			Label:      psensor.Label,
			Type:       psensor.Type,
			Data:       psensor.Data,
			Index:      psensor.Index,
			Schema:     psensor.Schema,
			Config:     psensor.Config,
			Param:      psensor.Param,
			CreateTime: &now,
			UpdateTime: &now,
		})
	}
	return &models.SysDevConfig{
			Id:         utils.SimpleUUID(),
			DevSn:      sn,
			Data:       []byte(config),
			Schema:     utils.MapToBytes(utils.GetObjectSchema(utils.BytesToMap([]byte(config)))["properties"].(map[string]interface{})),
			CreateTime: &now,
			UpdateTime: &now,
		},
		sensors,
		&models.SysDevSystem{
			Id:         utils.SimpleUUID(),
			DevSn:      sn,
			Data:       []byte(utils.If(devType == enums.DevTypeOfDevice, constant.DevSystemTemplate, constant.GatewaySystemTemplate).(string)),
			CreateTime: &now,
			UpdateTime: &now,
		},
		&models.SysDevFunc{
			Id:         utils.SimpleUUID(),
			DevSn:      sn,
			Data:       []byte(funcs),
			Schema:     utils.MapToBytes(utils.GetObjectSchema(utils.BytesToMap([]byte(funcs)))["properties"].(map[string]interface{})),
			CreateTime: &now,
			UpdateTime: &now,
		}
}

// 添加设备
func (d *Device) AddDevice(req *command.DeviceInsertReq) (err error, dev models.SysDev) {
	if !req.FromLoginInit && req.DeptId == constant.PublicGroupId && req.UserId != "1" {
		err = errors.New("没权限操作公共分组资源")
		return
	}
	var fromProductSensors []models.SysDevSensor
	var formProductConfig models.SysDevConfig
	var formProductFunc models.SysDevFunc
	if fromProductSensors, formProductConfig, formProductFunc, err = req.Generate(d.Orm, &dev); err != nil {
		return
	}
	if err = d.Orm.Transaction(func(tx *gorm.DB) error {
		// 事务orm
		d.sysDevModel.Orm = tx
		d.devConfigModel.Orm = tx
		d.devFuncModel.Orm = tx
		d.devSensorModel.Orm = tx
		d.devSystemModel.Orm = tx
		// 需要自动创建的产品
		if err = autoDevProduct(d.Orm, req.AutoProduct); err != nil {
			return err
		}
		// 添加设备基本信息
		if err = d.sysDevModel.AddDevice(&dev, req.FromLoginInit); err != nil {
			return err
		}
		devConfig, devSensors, devSystem, devFunc := generateProfile(d.Orm, &dev, formProductConfig, fromProductSensors, formProductFunc, req)
		// 设备profile.config
		if err = d.devConfigModel.Add(devConfig); err != nil {
			return err
		}
		// 设备func
		if err = d.devFuncModel.Add(devFunc); err != nil {
			return err
		}
		// 设备profile.sensor
		if len(devSensors) > 0 {
			if err = d.devSensorModel.Add(devSensors); err != nil {
				return err
			}
			// sensor缓存
			tmpSensor := make(map[string]interface{})
			for _, sensor := range devSensors {
				tmpSensor[sensor.Key] = nil
			}
			constant.SensorCache.Set(dev.Sn, tmpSensor)
		}
		// 设备profile.system
		if err = d.devSystemModel.Add(devSystem); err != nil {
			return err
		}
		// 缓存设备分组
		_ = env.Cache.HashSet(constant.DeviceDeptCache, map[string]interface{}{dev.Sn: dev.DeptId})
		// 缓存设备信息
		_ = env.Cache.HashSet(constant.DeviceEasyCache, map[string]interface{}{dev.Sn: string(utils.MapToBytes(map[string]interface{}{
			"type":         dev.Type,
			"name":         dev.Name,
			"productModel": dev.ProductModel,
			"gateway":      dev.GatewaySn,
		}))})
		if len(dev.GatewaySn) > 0 && dev.Sn != dev.GatewaySn {
			constant.DevGateCache.Set(dev.Sn, dev.GatewaySn)
			if devSns, ok := constant.GateDevsCache.Get(dev.GatewaySn); ok {
				ameda.StringsPush(&devSns, dev.Sn)
				constant.GateDevsCache.Set(dev.GatewaySn, devSns)
			} else {
				constant.GateDevsCache.Set(dev.GatewaySn, []string{dev.Sn})
			}
		} else if len(dev.ProductModel) > 0 && dev.Type != enums.DevTypeOfDevice {
			constant.GateModelCache.Set(dev.Sn, dev.ProductModel)
		}
		// 广播消息
		mqtt.MqttClient.Publish(constant.DeviceAddMsgTopic, utils.MapToBytes(map[string]interface{}{"sn": dev.Sn, "productCode": dev.ProductModel}))
		return nil
	}); err != nil {
		return
	}
	return
}

// 编辑设备
func (d *Device) ModDevice(req *command.DeviceEditReq) (err error) {
	if err = d.sysDevModel.FindOne(d.sysDevModel, cDto.MakeCondition(query.SysDevQuery{IdSn: req.Id, Type: req.Type})); err != nil {
		return
	}
	if d.sysDevModel.DeptId == constant.PublicGroupId && req.UserId != "1" {
		return errors.New("没权限操作公共分组资源")
	}
	changeModel := req.ProductModel != d.sysDevModel.ProductModel
	if err = d.Orm.Transaction(func(tx *gorm.DB) error {
		d.sysDevModel.Orm = tx
		// 可编辑项
		if len(req.Name) > 0 {
			d.sysDevModel.Name = req.Name
		}
		if len(req.DeptId) > 0 {
			d.sysDevModel.DeptId = req.DeptId
		}
		if len(req.Location) > 0 {
			d.sysDevModel.Location = utils.MapToBytes(req.Location)
		}
		if req.View != nil {
			d.sysDevModel.View = utils.MapToBytes(utils.If(len(req.View) > 0, req.View, map[string]interface{}{}).(map[string]interface{}))
		}
		if changeModel {
			// 切换产品
			if err = changeDevProduct(tx, d.sysDevModel, req.ProductModel); err != nil {
				return err
			}
		}
		// 编辑dev
		if err = d.sysDevModel.Mod(d.sysDevModel); err != nil {
			return err
		}
		// 缓存设备分组
		err = env.Cache.HashSet(constant.DeviceDeptCache, map[string]interface{}{d.sysDevModel.Sn: d.sysDevModel.DeptId})
		// 缓存设备信息
		err = env.Cache.HashSet(constant.DeviceEasyCache, map[string]interface{}{d.sysDevModel.Sn: string(utils.MapToBytes(map[string]interface{}{
			"type":         d.sysDevModel.Type,
			"name":         d.sysDevModel.Name,
			"productModel": d.sysDevModel.ProductModel,
			"gateway":      d.sysDevModel.GatewaySn,
		}))})
		if changeModel {
			// 广播消息
			mqtt.MqttClient.Publish(constant.DeviceAddMsgTopic, utils.MapToBytes(map[string]interface{}{"sn": d.sysDevModel.Sn, "productCode": d.sysDevModel.ProductModel}))
			if req.Type != enums.DevTypeOfDevice {
				constant.GateModelCache.Set(d.sysDevModel.Sn, d.sysDevModel.ProductModel)
			}
		}
		return nil
	}); err != nil {
		return
	}
	return
}

// 初始化设备名称（产品名称+SN后4位）
func (d *Device) InitName4Product(req *query.SysDevQuery, sns *[]string) (err error) {
	//查询无效的设备名称（name=sn）
	list := make([]models.SysDev, 0)
	d.productModel = &models.SysDevProduct{}
	d.productModel.Orm = d.Orm
	d.sysDevModel.FindListByConds(&list, "(sn = name or name = '') and type = 'device' and product_model not in('ssiot-applet-sysinfo','SS-DEFAULT','ssiot-device','ipcamera')")
	if len(list) == 0 {
		return nil
	}

	//查询产品
	pms := make([]string, 0)
	for _, dev := range list {
		pms = append(pms, dev.ProductModel)
	}
	plist := make([]models.SysDevProduct, 0)
	d.productModel.FindListByConds(&plist, "model in (?)", pms)
	pm := make(map[string]models.SysDevProduct)
	for _, pd := range plist {
		pm[pd.ProductModel] = pd
	}
	devSns := make([]string, 0)
	if err := d.Orm.Transaction(func(tx *gorm.DB) error {
		d.sysDevModel.Orm = tx
		//设置设备名称
		for _, dev := range list {
			length := len(dev.Sn)
			prefix := pm[dev.ProductModel].Name
			var suffix string
			if length < 4 {
				suffix = dev.Sn
			} else {
				suffix = dev.Sn[length-4:]
			}
			dev.Name = strings.ToUpper(fmt.Sprintf("%s%s", prefix, suffix))
			d.sysDevModel.Mod(dev)

			// 缓存设备信息
			err = env.Cache.HashSet(constant.DeviceEasyCache, map[string]interface{}{d.sysDevModel.Sn: string(utils.MapToBytes(map[string]interface{}{
				"type":         d.sysDevModel.Type,
				"name":         d.sysDevModel.Name,
				"productModel": d.sysDevModel.ProductModel,
				"gateway":      d.sysDevModel.GatewaySn,
			}))})
			devSns = append(devSns, dev.Sn)
		}
		*sns = *(&devSns)
		return nil
	}); err != nil {
		return err
	}

	return nil
}

// 删除设备
func (d *Device) DeleteDevice(req *command.DeviceDeleteReq) (err error) {
	//delIdSns := strings.Split(strings.ReplaceAll(req.IdSn, " ", ""), ",")
	//if req.DelType == enums.DevTypeOfSoftGateway && len(delIdSns) > 1 {
	//	err = errors.New("软网关不支持批量删除")
	//	return
	//}
	var delDevList []models.SysDev
	if err = d.Orm.Transaction(func(tx *gorm.DB) error {
		// 事务orm
		d.sysDevModel.Orm = tx
		d.devConfigModel.Orm = tx
		d.devFuncModel.Orm = tx
		d.devSensorModel.Orm = tx
		d.devSystemModel.Orm = tx
		// 批量操作
		snList := make([]string, 0)
		if delDevList, err = getBatchDelDev(d.sysDevModel, req, &snList); err != nil {
			return err
		}
		if len(snList) == 0 {
			return nil
		}
		// 设备基本信息
		if err = d.sysDevModel.Delete(&models.SysDev{}, cDto.MakeCondition(query.SysDevQuery{SnIn: snList})); err != nil {
			return err
		}
		// 设备profile.config
		if err = d.devConfigModel.Delete(&models.SysDevConfig{}, cDto.MakeCondition(query.SysDevConfigQuery{SnIn: snList})); err != nil {
			return err
		}
		if err = d.devFuncModel.Delete(&models.SysDevFunc{}, cDto.MakeCondition(query.SysDevFuncQuery{SnIn: snList})); err != nil {
			return err
		}
		// 设备profile.sensor
		if err = d.devSensorModel.Delete(&models.SysDevSensor{}, cDto.MakeCondition(query.SysDevSensorQuery{SnIn: snList})); err != nil {
			return err
		}
		// 设备profile.system
		if err = d.devSystemModel.Delete(&models.SysDevSystem{}, cDto.MakeCondition(query.SysDevSystemQuery{SnIn: snList})); err != nil {
			return err
		}

		// 删分组/设备缓存/sensor缓存
		for _, s := range snList {
			_ = env.Cache.HashDel(constant.DeviceDeptCache, s)
			_ = env.Cache.HashDel(constant.DeviceEasyCache, s)
			constant.SensorCache.Remove(s)
			constant.SensorInsCache.Remove(s)
			constant.DevOnlineCache.Remove(s)
			constant.StatusSnCache.Remove(s)
			constant.DevGateCache.Remove(s)
			constant.AutoLoginSn.Delete(s)
			constant.GateModelCache.Remove(s)
			if devs, ok := constant.GateDevsCache.Get(s); ok {
				constant.GateDevsCache.Remove(s)
				for _, devSn := range devs {
					constant.DevOnlineCache.Remove(devSn)
					constant.StatusSnCache.Remove(devSn)
				}
			} else {
				for tp := range constant.GateDevsCache.IterBuffered() {
					gateSn := tp.Key
					devSns := tp.Val
					if utils.StrIn(s, devSns) {
						if devsLen := ameda.StringsRemoveFirst(&devSns, s); devsLen == 0 {
							constant.GateDevsCache.Remove(gateSn)
						} else {
							constant.GateDevsCache.Set(gateSn, devSns)
						}
					}
				}
			}
			for tp := range constant.SensorSnKey.IterBuffered() {
				key := tp.Key
				if strings.HasPrefix(key, s+"#") {
					constant.SensorSnKey.Remove(key)
				}
			}
		}
		// 广播消息
		mqtt.MqttClient.Publish(constant.DeviceDeleteMsgTopic, utils.MapToBytes(map[string]interface{}{"sn": strings.Join(snList, ",")}))
		return nil
	}); err != nil {
		return
	}
	// 下发空配置
	for _, v := range delDevList {
		setEmptyConfig(v)
	}
	return
}

func getBatchDelDev(d *models.SysDev, req *command.DeviceDeleteReq, snList *[]string) (devList []models.SysDev, err error) {
	// 查询设备
	devList = make([]models.SysDev, 0)
	delIdSns := strings.Split(strings.ReplaceAll(req.IdSn, " ", ""), ",")
	if err = d.FindList(&devList, cDto.MakeCondition(query.SysDevQuery{IdSnIn: delIdSns})); err != nil {
		return
	}
	// sn-gate关系
	for _, v := range devList {
		*snList = append(*snList, v.Sn)
		if v.DeptId == constant.PublicGroupId && req.UserId != "1" {
			err = errors.New("没权限操作公共分组资源")
			return
		}
	}
	//判断网关能否删
	if req.DelType != enums.DevTypeOfDevice {
		var count int64
		if err = d.FindCount(d, &count, cDto.MakeCondition(query.SysDevQuery{GatewaySnIn: *snList})); err != nil {
			return
		}
		if count > 0 {
			err = errors.New("网关存在下位机配置, 无法删除")
		}
	}
	return
}

func setEmptyConfig(sysDev models.SysDev) {
	var gateSn, to string
	if sysDev.Type == enums.DevTypeOfDevice {
		gateSn = utils.If(len(sysDev.GatewaySn) > 0, sysDev.GatewaySn, sysDev.Sn).(string)
		to = sysDev.Sn
	} else {
		gateSn = sysDev.Sn
		to = ""
	}
	// 下发终端
	topic := fmt.Sprintf("/iot/%s/%s/config/set", gateSn, constant.TopicPlaceholder)
	content := make(map[string]interface{})
	sid := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(65535)
	content["sid"] = sid
	content["from"] = constant.TopicPlaceholder
	content["to"] = to
	content["data"] = nil
	contentBytes, _ := sonic.Marshal(content)
	mqtt.MqttClient.Publish(topic, contentBytes)
}

// 批量分组
func (d *Device) ChangeGroup(req *command.BatchGroupReq) error {
	idList := strings.Split(strings.ReplaceAll(req.IdSn, " ", ""), ",")
	devices := make([]models.SysDev, 0)
	condition := cDto.MakeCondition(query.SysDevQuery{IdSnIn: idList})
	if err := d.sysDevModel.FindList(&devices, condition); err != nil {
		return err
	}
	if len(devices) == 0 {
		return errors.New("设备不存在")
	}
	hmsetMap := make(map[string]interface{})
	for _, v := range devices {
		hmsetMap[v.Sn] = req.DeptId
	}
	if err := d.Orm.Transaction(func(tx *gorm.DB) error {
		d.sysDevModel.Orm = tx
		if err := d.sysDevModel.ModMap(d.sysDevModel, map[string]interface{}{"dept_id": req.DeptId}, condition); err != nil {
			return err
		}
		if err := env.Cache.HashSet(constant.DeviceDeptCache, hmsetMap); err != nil {
			return errors.New("缓存出现异常" + err.Error())
		}
		return nil
	}); err != nil {
		return err
	}
	return nil
}

// 设备配置下发
func (d *Device) SetDeviceConfig(req *command.DevicePropsSetReq) (err error) {
	sysDev := models.SysDev{}
	if err = d.sysDevModel.FindOne(&sysDev, cDto.MakeCondition(query.SysDevQuery{IdSn: req.IdSn})); err != nil {
		return
	}
	if sysDev.DeptId == constant.PublicGroupId && req.UserId != "1" {
		err = errors.New("没权限操作公共分组资源")
		return
	}
	// 获取设备配置
	if err = d.devConfigModel.FindDevConfig(&query.SysDevConfigQuery{Sn: sysDev.Sn}); err != nil {
		return
	}
	configData := utils.BytesToMap(d.devConfigModel.Data)["config"].(map[string]interface{})
	var gateSn, to string
	if sysDev.Type == enums.DevTypeOfDevice {
		gateSn = utils.If(len(sysDev.GatewaySn) > 0, sysDev.GatewaySn, sysDev.Sn).(string)
		to = sysDev.Sn
	} else {
		gateSn = sysDev.Sn
		to = ""
		//// 组装子设备config
		//var subdeviceJson map[string]interface{}
		//if subdeviceJson, err = d.devConfigModel.GetSubDeviceConfig(gateSn); err != nil {
		//	return
		//}
		//configData["subdevice"] = subdeviceJson
	}
	// 下发终端
	topic := fmt.Sprintf("/iot/%s/%s/config/set", gateSn, constant.TopicPlaceholder)
	content := make(map[string]interface{})
	sid := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(65535)
	content["sid"] = sid
	content["from"] = constant.TopicPlaceholder
	content["to"] = to
	content["data"] = configData
	contentBytes, _ := sonic.Marshal(content)
	if req.Async {
		mqtt.MqttClient.Publish(topic, contentBytes)
	} else {
		// 同步处理, 订阅响应
		subT := fmt.Sprintf("/iot/%s/%s/config/set/ack", constant.TopicPlaceholder, gateSn)
		// 同步下发
		if err = mqtt.MqttClient.SyncSend(topic, contentBytes, sid, subT, func(msgInfo map[string]interface{}, e *error) {}); err != nil {
			syncSendErrCallback(subT, sid)
		}
	}
	return
}

// 查询设备配置
func (d *Device) GetDeviceConfig(req *command.DevicePropsGetReq) (err error, result interface{}) {
	sysDev := models.SysDev{}
	if err = d.sysDevModel.FindOne(&sysDev, cDto.MakeCondition(query.SysDevQuery{IdSn: req.IdSn})); err != nil {
		return
	}
	var gateSn, to string
	if sysDev.Type == enums.DevTypeOfDevice {
		gateSn = utils.If(len(sysDev.GatewaySn) > 0, sysDev.GatewaySn, sysDev.Sn).(string)
		to = sysDev.Sn
	} else {
		gateSn = sysDev.Sn
		to = ""
	}
	// 下发终端
	topic := fmt.Sprintf("/iot/%s/%s/config/get", gateSn, constant.TopicPlaceholder)
	content := make(map[string]interface{})
	sid := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(65535)
	content["sid"] = sid
	content["from"] = constant.TopicPlaceholder
	content["to"] = to
	content["data"] = map[string]interface{}{
		"path": req.Path,
	}
	contentBytes, _ := sonic.Marshal(content)
	if req.Async {
		mqtt.MqttClient.Publish(topic, contentBytes)
	} else {
		// 同步处理, 订阅响应
		subT := fmt.Sprintf("/iot/%s/%s/config/get/ack", constant.TopicPlaceholder, gateSn)
		// 同步下发
		if err = mqtt.MqttClient.SyncSend(topic, contentBytes, sid, subT, func(msgInfo map[string]interface{}, e *error) {
			// 响应回调处理
			result = msgInfo["data"]
			if result == nil {
				return
			}
			dataStr := string(utils.MapToBytes(result.(map[string]interface{})))
			path := "{config}"
			if len(req.Path) > 0 {
				path = "{config"
				for _, v := range strings.Split(req.Path, ".") {
					path += "," + v
				}
				path += "}"
			}
			sql := "update sys_dev_config set data = jsonb_set(data::jsonb, '" + path + "', jsonb_update(data #> '" + path + "', '" + dataStr + "' :: jsonb ) ) where dev_sn = '%s'"
			// 防止字符串中存在?占位符
			sql = fmt.Sprintf(sql, sysDev.Sn)
			err = d.Orm.Transaction(func(tx *gorm.DB) error {
				// 更新config
				if err = tx.Raw(sql).Scan(&models.SysDevConfig{}).Error; err != nil {
					logger.Error(err)
				}
				return nil
			})
		}); err != nil {
			syncSendErrCallback(subT, sid)
		}
	}
	return
}

// 设备变量下发
func (d *Device) SetDeviceSensor(req *command.DeviceSensorSetReq) (err error) {
	sysDev := models.SysDev{}
	if err = d.sysDevModel.FindOne(&sysDev, cDto.MakeCondition(query.SysDevQuery{IdSn: req.IdSn})); err != nil {
		return
	}
	if sysDev.DeptId == constant.PublicGroupId && req.UserId != "1" {
		err = errors.New("没权限操作公共分组资源")
		return
	}
	if req.Local != nil && *req.Local {
		// 仅更新不下发(如平台变量设值)
		err = d.devSensorModel.UpdateDevSensor(sysDev.Sn, req.Data, req.Entire != nil && *req.Entire)
		return
	}
	var gateSn, to string
	if sysDev.Type == enums.DevTypeOfDevice {
		gateSn = utils.If(len(sysDev.GatewaySn) > 0, sysDev.GatewaySn, sysDev.Sn).(string)
		to = sysDev.Sn
	} else {
		gateSn = sysDev.Sn
		to = ""
	}
	// 下发终端
	topic := fmt.Sprintf("/iot/%s/%s/sensor/set", gateSn, constant.TopicPlaceholder)
	content := make(map[string]interface{})
	sid := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(65535)
	content["sid"] = sid
	content["from"] = constant.TopicPlaceholder
	content["to"] = to
	content["data"] = req.Data
	contentBytes, _ := sonic.Marshal(content)
	if req.Async {
		mqtt.MqttClient.Publish(topic, contentBytes)
	} else {
		// 同步处理, 订阅响应
		subT := fmt.Sprintf("/iot/%s/%s/sensor/set/ack", constant.TopicPlaceholder, gateSn)
		// 同步下发
		if err = mqtt.MqttClient.SyncSend(topic, contentBytes, sid, subT, func(msgInfo map[string]interface{}, e *error) {
			// 下发成功后, 更新实时数据 - profile变量值
			if err = d.devSensorModel.UpdateDevSensor(sysDev.Sn, req.Data); err != nil {
				return
			}
		}); err != nil {
			syncSendErrCallback(subT, sid)
		}
	}
	return
}

// 设备变量查询
func (d *Device) GetDeviceSensor(req *command.DeviceSensorGetReq) (result interface{}, err error) {
	sysDev := models.SysDev{}
	if err = d.sysDevModel.FindOne(&sysDev, cDto.MakeCondition(query.SysDevQuery{IdSn: req.IdSn})); err != nil {
		return
	}
	if sysDev.DeptId == constant.PublicGroupId && req.UserId != "1" {
		err = errors.New("没权限操作公共分组资源")
		return
	}
	var gateSn, to string
	if sysDev.Type == enums.DevTypeOfDevice {
		gateSn = utils.If(len(sysDev.GatewaySn) > 0, sysDev.GatewaySn, sysDev.Sn).(string)
		to = sysDev.Sn
	} else {
		gateSn = sysDev.Sn
		to = ""
	}
	// 下发终端
	topic := fmt.Sprintf("/iot/%s/%s/sensor/get", gateSn, constant.TopicPlaceholder)
	content := make(map[string]interface{})
	sid := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(65535)
	content["sid"] = sid
	content["from"] = constant.TopicPlaceholder
	content["to"] = to
	content["data"] = map[string]interface{}{
		"path": utils.InterfaceToStr(req.Path),
	}
	contentBytes, _ := sonic.Marshal(content)
	if req.Async {
		mqtt.MqttClient.Publish(topic, contentBytes)
	} else {
		// 同步处理, 订阅响应
		subT := fmt.Sprintf("/iot/%s/%s/sensor/get/ack", constant.TopicPlaceholder, gateSn)
		// 同步下发
		if err = mqtt.MqttClient.SyncSend(topic, contentBytes, sid, subT, func(msgInfo map[string]interface{}, e *error) {
			result = msgInfo["data"]
			if result == nil {
				return
			}
			m, ok := result.(map[string]interface{})
			if !ok || len(m) == 0 {
				return
			}
			// 响应成功后, 更新实时数据 - profile变量值
			if err = d.devSensorModel.UpdateDevSensor(sysDev.Sn, m); err != nil {
				return
			}
		}); err != nil {
			syncSendErrCallback(subT, sid)
		}
	}
	return
}

// 设备下发控制
func (d *Device) SendDeviceFunc(req *command.DeviceFuncSetReq) (result interface{}, err error) {
	sysDev := models.SysDev{}
	if err = d.sysDevModel.FindOne(&sysDev, cDto.MakeCondition(query.SysDevQuery{IdSn: req.IdSn})); err != nil {
		return
	}
	if sysDev.DeptId == constant.PublicGroupId && req.UserId != "1" {
		err = errors.New("没权限操作公共分组资源")
		return
	}
	var gateSn, to string
	if sysDev.Type == enums.DevTypeOfDevice {
		gateSn = utils.If(len(sysDev.GatewaySn) > 0, sysDev.GatewaySn, sysDev.Sn).(string)
		to = sysDev.Sn
	} else {
		gateSn = sysDev.Sn
		to = ""
	}
	// 下发终端
	topic := fmt.Sprintf("/iot/%s/%s/func/%s", gateSn, constant.TopicPlaceholder, req.FuncName)
	content := make(map[string]interface{})
	sid := rand.New(rand.NewSource(time.Now().UnixNano())).Intn(65535)
	content["sid"] = sid
	content["from"] = constant.TopicPlaceholder
	content["to"] = to
	content["data"] = req.Data
	contentBytes, _ := sonic.Marshal(content)
	if req.Async {
		mqtt.MqttClient.Publish(topic, contentBytes)
	} else {
		// 同步处理, 订阅响应
		subT := fmt.Sprintf("/iot/%s/%s/func/%s/ack", constant.TopicPlaceholder, gateSn, req.FuncName)
		// 同步下发
		if req.Latency != nil && *req.Latency > 0 {
			// 指定超时时间
			overtime := time.Duration(*req.Latency) * time.Second
			if err = mqtt.MqttClient.SyncSendTimer(topic, contentBytes, sid, overtime, subT, func(msgInfo map[string]interface{}, e *error) {
				result = msgInfo["data"]
			}); err != nil {
				syncSendErrCallback(subT, sid)
			}
		} else {
			if err = mqtt.MqttClient.SyncSend(topic, contentBytes, sid, subT, func(msgInfo map[string]interface{}, e *error) {
				result = msgInfo["data"]
			}); err != nil {
				syncSendErrCallback(subT, sid)
			}
		}
	}
	return
}

func syncSendErrCallback(subT string, sid int) {
	mqtt.DynamicSubTopicCnt.Upsert(subT, nil, func(exist bool, valueInMap map[int]func(map[string]interface{}, *error), newValue map[int]func(map[string]interface{}, *error)) map[int]func(map[string]interface{}, *error) {
		if !exist || valueInMap == nil {
			return valueInMap
		}
		delete(valueInMap, sid)
		if len(valueInMap) == 0 {
			mqtt.MqttClient.UnSubscribe(subT)
		}
		return valueInMap
	})
}

// 查询设备profile
func (d *Device) FindDevProfile(req *query.SysDevProfileQuery) (err error, result map[string]interface{}) {
	result = map[string]interface{}{}
	profile := map[string]interface{}{}
	props := map[string]interface{}{}
	sensors := map[string]interface{}{}
	profile["props"] = props
	profile["sensor"] = sensors
	result["profile"] = profile
	if len(req.IdSn) > 0 {
		if err = d.sysDevModel.FindOne(d.sysDevModel, cDto.MakeCondition(query.SysDevQuery{IdSn: req.IdSn})); err != nil {
			return
		}
		result["devSn"] = d.sysDevModel.Sn
		result["productModel"] = d.sysDevModel.ProductModel
		result["createTime"] = d.sysDevModel.CreateTime
		result["updateTime"] = d.sysDevModel.UpdateTime
		// 查询config
		if err = d.devConfigModel.FindOne(d.devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{Sn: d.sysDevModel.Sn})); err != nil {
			return
		}
		utils.DeepMerge(utils.BytesToMap(d.devConfigModel.Data), profile)
		// 查询func
		if err = d.devFuncModel.FindOne(d.devFuncModel, cDto.MakeCondition(query.SysDevFuncQuery{Sn: d.sysDevModel.Sn})); err != nil {
			return
		}
		utils.DeepMerge(utils.BytesToMap(d.devFuncModel.Data), profile)
		// 基本信息
		props["basic"] = utils.JsoniterToMap(*d.sysDevModel)
		// 查询sensor
		if dataMap, ok := constant.SensorCache.Get(d.sysDevModel.Sn); ok && dataMap != nil {
			utils.DeepMerge(dataMap, sensors)
		} else {
			ss := make([]models.SysDevSensor, 0)
			if err = d.devSensorModel.FindList(&ss, cDto.MakeCondition(query.SysDevSensorQuery{Sn: d.sysDevModel.Sn})); err != nil {
				return
			}
			for _, sen := range ss {
				utils.DeepMerge(utils.BytesToMap(sen.Data), sensors)
			}
		}
		// 查询system
		if err = d.devSystemModel.FindOne(d.devSystemModel, cDto.MakeCondition(query.SysDevSystemQuery{Sn: d.sysDevModel.Sn})); err != nil {
			return
		}
		utils.DeepMerge(utils.BytesToMap(d.devSystemModel.Data), props)
	} else if len(req.ProductId) > 0 {
		if err = d.productModel.FindOne(d.productModel, cDto.MakeCondition(query.SysProductQuery{Id: req.ProductId})); err != nil {
			return
		}
		result["devSn"] = ""
		result["productModel"] = d.productModel.ProductModel
		result["devProductId"] = d.productModel.Id
		result["createTime"] = d.productModel.CreateTime
		result["updateTime"] = d.productModel.UpdateTime
		// 查询config
		if err = d.devConfigModel.FindOne(d.devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{ProductId: d.productModel.Id})); err != nil {
			return
		}
		utils.DeepMerge(utils.BytesToMap(d.devConfigModel.Data), profile)
		// 查询func
		if err = d.devFuncModel.FindOne(d.devFuncModel, cDto.MakeCondition(query.SysDevFuncQuery{ProductId: d.productModel.Id})); err != nil {
			return
		}
		utils.DeepMerge(utils.BytesToMap(d.devFuncModel.Data), profile)
		// 基本信息
		props["basic"] = utils.JsoniterToMap(*d.productModel)
		// system
		props["system"] = map[string]interface{}{}
		// sensor
		profile["sensor"] = map[string]interface{}{}
	} else {
		err = errors.New("查询异常")
		return
	}
	// path处理
	if len(req.Path) > 0 {
		paths := strings.Split(req.Path, ".")
		result["profile"] = utils.GetFieldFromJson(paths, profile)
	}
	return
}

// 查询多设备历史数据
func (d *Device) FindDeviceHistoryData(req *query.DevDataQuery) (err error, result interface{}) {
	// 不允许都不传
	if len(req.IdSn) == 0 && len(req.ProductModel) == 0 {
		return
	}
	// 根据条件定位sns
	q := query.SysDevQuery{}
	q.UserInfo = req.UserInfo
	if len(req.IdSn) > 0 {
		q.IdSnIn = strings.Split(strings.ReplaceAll(req.IdSn, " ", ""), ",")
	}
	if len(req.ProductModel) > 0 {
		q.ProductModelIn = strings.Split(strings.ReplaceAll(req.ProductModel, " ", ""), ",")
	}
	var devList []models.SysDev
	var devSns []string
	if err = d.Orm.Model(&devList).Scopes(cDto.MakeCondition(q.GetNeedSearch())).Distinct().Pluck("sn", &devSns).Error; err != nil {
		return
	}
	return processHistoryData(d.Orm, req, devSns)
}

// 查询设备历史数据
func (d *Device) GetDeviceHistoryData(req *query.DevDataQuery) (err error, result interface{}) {
	if len(req.IdSn) == 0 {
		return
	}
	if err = d.sysDevModel.FindOne(d.sysDevModel, cDto.MakeCondition(query.SysDevQuery{IdSn: req.IdSn})); err != nil {
		return
	}
	return processHistoryData(d.Orm, req, []string{d.sysDevModel.Sn})
}

// 查询设备历史数据多变量按事件行转列
func (d *Device) GetDeviceHistoryDataReport(req *query.DevHistStatQuery) (err error, result interface{}) {
	return processHistoryDataReport(d.Orm, req)
}

func (d *Device) FindDeviceHistorySystem(req *query.DevDataQuery) (err error, result interface{}) {
	// 不允许都不传
	if len(req.IdSn) == 0 && len(req.ProductModel) == 0 {
		return
	}
	// 根据条件定位sns
	q := query.SysDevQuery{}
	q.UserInfo = req.UserInfo
	if len(req.IdSn) > 0 {
		q.IdSnIn = strings.Split(strings.ReplaceAll(req.IdSn, " ", ""), ",")
	}
	if len(req.ProductModel) > 0 {
		q.ProductModelIn = strings.Split(strings.ReplaceAll(req.ProductModel, " ", ""), ",")
	}
	var devList []models.SysDev
	var devSns []string
	if err = d.Orm.Model(&devList).Scopes(cDto.MakeCondition(q.GetNeedSearch())).Distinct().Pluck("sn", &devSns).Error; err != nil {
		return
	}
	return processHistorySystem(d.Orm, req, devSns)
}

// 查询设备历史数据
func (d *Device) GetDeviceHistorySystem(req *query.DevDataQuery) (err error, result interface{}) {
	if len(req.IdSn) == 0 {
		return
	}
	if err = d.sysDevModel.FindOne(d.sysDevModel, cDto.MakeCondition(query.SysDevQuery{IdSn: req.IdSn})); err != nil {
		return
	}
	return processHistorySystem(d.Orm, req, []string{d.sysDevModel.Sn})
}

// 设备保存为产品
func (d *Device) DeviceSaveProduct(req *command.DeviceSaveProductReq) (err error) {
	sysDev := models.SysDev{}
	if err = d.sysDevModel.FindOne(&sysDev, cDto.MakeCondition(query.SysDevQuery{IdSn: req.IdSn})); err != nil {
		return
	}
	if sysDev.DeptId == constant.PublicGroupId && req.UserId != "1" {
		return errors.New("没权限操作公共分组资源")
	}
	productInsert := command.ProductInsertReq{}
	if err = req.GenerateProduct(&sysDev, &productInsert); err != nil {
		return
	}
	// 查询设备sensor
	sensors := make([]models.SysDevSensor, 0)
	if err = d.devSensorModel.FindList(&sensors, cDto.MakeCondition(query.SysDevSensorQuery{Sn: sysDev.Sn})); err != nil {
		return
	}
	// 查询设备config
	if err = d.devConfigModel.FindOne(d.devConfigModel, cDto.MakeCondition(query.SysDevConfigQuery{Sn: sysDev.Sn})); err != nil {
		return
	}
	// 查询设备func
	if err = d.devFuncModel.FindOne(d.devFuncModel, cDto.MakeCondition(query.SysDevFuncQuery{Sn: sysDev.Sn})); err != nil {
		return
	}
	productInsert.DevSensors = sensors
	productInsert.DevConfig = d.devConfigModel
	productInsert.DevFunc = d.devFuncModel
	// 添加产品
	sp := &Product{}
	sp.Orm = d.Orm
	sp.SetAllModel()
	err, _ = sp.AddProduct(&productInsert)
	return
}

// 配置整体操作
func (d *Device) UpsertEntireConfig(req *command.DeviceConfigReq) (err error) {
	sysDev := models.SysDev{}
	if err = d.sysDevModel.FindOne(&sysDev, cDto.MakeCondition(query.SysDevQuery{IdSn: req.IdSn, Type: req.DeviceType})); err != nil {
		return
	}
	if sysDev.DeptId == constant.PublicGroupId && req.UserId != "1" {
		return errors.New("没权限操作公共分组资源")
	}
	if len(req.Data) == 0 && len(req.Schema) == 0 {
		return
	}
	// 整体替换网关的profile.config
	path := "{config}"
	schemaPath := "{config,properties}"
	if len(req.NodePath) > 0 {
		path = "{" + strings.ReplaceAll(req.NodePath, ".", ",") + "}"
		schemaPath = "{" + strings.ReplaceAll(req.NodePath, ".", ",properties,") + "}"
	}
	// 支持data、schema分别修改
	sql := "update sys_dev_config set "
	sets := make([]string, 0)
	if len(req.Data) > 0 {
		dataStr := string(utils.MapToBytes(req.Data))
		sets = append(sets, "data = jsonb_set(data::jsonb, '"+path+"', '"+dataStr+"' :: jsonb )")
	}
	if len(req.Schema) > 0 {
		schemaStr := string(utils.MapToBytes(req.Schema))
		sets = append(sets, "schema = jsonb_set(schema::jsonb, '"+schemaPath+"', '"+schemaStr+"' :: jsonb )")
	}
	setsql := strings.Join(sets, ",")
	sql += setsql
	sql += " where dev_sn = '" + sysDev.Sn + "' "
	if err = d.Orm.Raw(sql).Scan(d.devConfigModel).Error; err != nil {
		return err
	}
	return
}

var scan = map[string]interface{}{}

// 更新设备协议字段
func UpdateDevProtocol(db *gorm.DB, devSn ...string) error {
	snProtoMap := make(map[string]string)
	if len(devSn) > 0 {
		for _, sn := range devSn {
			gate, ok1 := constant.DevGateCache.Get(sn)
			gateModel, ok2 := constant.GateModelCache.Get(gate)
			if !ok1 || !ok2 {
				continue
			}
			protocolValue, ok3 := constant.ModelProtocolCache.Get(gateModel)
			if !ok3 {
				continue
			}
			snProtoMap[sn] = protocolValue
		}
	} else {
		for tp := range constant.DevGateCache.IterBuffered() {
			gateModel, ok2 := constant.GateModelCache.Get(tp.Val)
			if !ok2 || len(gateModel) == 0 {
				continue
			}
			protocolValue, ok3 := constant.ModelProtocolCache.Get(gateModel)
			if !ok3 {
				continue
			}
			snProtoMap[tp.Key] = protocolValue
		}
	}
	if len(snProtoMap) == 0 {
		return nil
	}
	// 确定设备protocol字段
	// 1、有网关 且 网关有产品编码 且找的到协议标识  (只更新字段空的)
	sql := "UPDATE sys_dev SET protocol = tmp.protocol FROM (VALUES %s) as tmp (sn, protocol) where sys_dev.sn = tmp.sn and (sys_dev.protocol is null or sys_dev.protocol = '')"
	valueSql := make([]string, 0)
	for sn, protocol := range snProtoMap {
		valueSql = append(valueSql, "('"+sn+"','"+protocol+"')")
	}
	sql = fmt.Sprintf(sql, strings.Join(valueSql, ","))
	if err := db.Raw(sql).Scan(&scan).Error; err != nil {
		return err
	}
	// 2、(无网关)设备、网关 统一更新为sansan
	sql = "UPDATE sys_dev SET protocol = 'sansan' where sys_dev.type != 'device' or (sys_dev.type = 'device' and (sys_dev.gateway_sn is null or sys_dev.gateway_sn = ''))"
	if err := db.Raw(sql).Scan(&scan).Error; err != nil {
		return err
	}
	return nil
}

func (d *Device) ChannelDiagnose(req *command.ChanelDiagnoseReq) (result interface{}) {
	tp := strings.ToLower(req.Type)
	switch tp {
	case "tcp":
		// tcp服务端
		port := cast.ToInt(req.Port)
		// 本地网络端口诊断
		connectionStats, _ := netutil.Connections("all")
		netresult := make([]map[string]interface{}, 0)
		for _, statv := range connectionStats {
			localPort := int(statv.Laddr.Port)
			if localPort != port {
				continue
			}
			remotePortStr := strconv.Itoa(int(statv.Raddr.Port))
			socketType := utils.If(statv.Type == syscall.SOCK_STREAM, "tcp", "udp").(string)
			fourSix := utils.If(statv.Family == syscall.AF_INET, "", "6").(string)
			netresult = append(netresult, map[string]interface{}{
				"type":       socketType + fourSix,
				"fd":         statv.Fd,
				"localAddr":  statv.Laddr.IP + ":" + strconv.Itoa(localPort),
				"remoteAddr": statv.Raddr.IP + ":" + utils.If(remotePortStr == "0", "*", remotePortStr).(string),
				"status":     statv.Status,
			})
		}
		//result = netresult
		result = utils.If(len(netresult) > 0, "Port "+req.Port+" Listening", "Port "+req.Port+" Not Listening.").(string)
	case "ethernet":
		// tcp客户端
		addresss := req.Port
		// 远程网络地址诊断
		timeout := time.Second * 2
		conn, err := net.DialTimeout("tcp", addresss, timeout)
		if err != nil {
			return "Remote Connection Fail. " + err.Error()
		}
		defer conn.Close()
		return "Remote Connection OK. " + conn.RemoteAddr().String()
	case "com":
		// 串口
		serialName := req.Port
		//本地串口诊断
		options := serial.OpenOptions{
			PortName: serialName,
			BaudRate: 9600,
			DataBits: 8,
			StopBits: 1,
		}
		port, err := serial.Open(options)
		if err != nil {
			return "Diagnose Serial Fail. " + err.Error()
		}
		defer port.Close()
		return "Diagnose Serial OK. "
	default:
		return "invalid type"
	}
	return
}
